#include "StdAfx.h"
#include "mlsparser.h"

CStringA CMLSParser::ErrMsg[]={"No errors","File error",
"Extra character on command","Unknown command",
"Command parameter missing","Illegal embeding nesting",
"Command parameter type-missmatch"," expected"};
char CMLSParser::JustComChar[] = {'L','R','C','B','N'};
BOOL CMLSParser::bIgnorError = FALSE;
CMLSParser::CommandRulesItem CMLSParser::MLSCommandRules[] =
{
	{"NJ",FALSE},
	{"LJ",FALSE},
	{"RJ",FALSE},
	{"CJ",FALSE},
	{"BJ",FALSE},
	{"BF",FALSE},
	{"FN",FALSE},
	{"HE",FALSE},
	{"FT",FALSE},
	{"TB",FALSE},
	{"PA",FALSE},
	{"PN",FALSE},
	{"PC",FALSE},
	{"BM",FALSE},
	{"CS",FALSE},
	{"LI",FALSE},
	{"PI",FALSE},
	{"LM",FALSE},
	{"LS",FALSE},
	{"NF",FALSE},
	{"NS",FALSE},
	{"NT",FALSE},
	{"PS",FALSE},
	{"RI",FALSE},
	{"RM",FALSE},
	{"SS",FALSE},
	{"ST",FALSE},
	{"TM",FALSE},
	{"UO",FALSE},
	{"UW",FALSE}
};
/*********************************************************************************
   Main Procedures
*********************************************************************************/
BOOL CMLSParser::ParseFile(CStringA FileName, CRichWriter* Target)
{
	Output = Target;
	InitializeOutput();
	BOOL res = Parse(FileName);
	FinalizeOutput();
	return res||bIgnorError;
}
BOOL CMLSParser::ParseChain(CMLSChain* Source, CRichWriter* Target)
{
	Output = Target;
	InitializeOutput();
	for(int n=0;n<Source->GetCount();n++)
	{
		if(n>0)
			Target->PageAdvance();
		Target->TraceState();
		if(!Parse(Source->GetItemPath(n))) return FALSE;
	}
	FinalizeOutput();
	return TRUE;
}
BOOL CMLSParser::ParseSource(CStringA FileName, CRichWriter* Target)
{
	return FALSE;
}
/*********************************************************************************
   Secondery Main Procedures
*********************************************************************************/
void CMLSParser::InitializeOutput()
{
	Output->StartDocument(CRichWriter::Arabic);
	Output->StartFontTable();
	Output->AddFont(CRTFWriter::ff_swiss,"Courier New");
	Output->AddFont(CRTFWriter::ff_swiss,"Andalus");
	Output->AddFont(CRTFWriter::ff_swiss,"Arial");
	Output->AddFont(CRTFWriter::ff_nill,"Athanasius",0);
	Output->AddFont(CRTFWriter::ff_swiss,"GreekC");
	Output->EndFontTable();
}
void CMLSParser::FinalizeOutput()
{
	Output->EndDocument();
}
BOOL CMLSParser::Parse(CStringA FileName)//Errors: File
{
	int n=FileName.ReverseFind('\\');
	CurrentFile=(n==-1)?FileName:FileName.Right(FileName.GetLength()-n-1);
	if(!Open(FileName))
	{
		Error(mpe_file);
		return FALSE;
	}
	do
	{
		if(GetTokenAhead()==mlstk_Semicolon)
		{
			if(!CommandBlock()) {Close();return FALSE;}
		}
		else if(bTaleDefined&&GetTokenAhead()==mlstk_Delimiter)
		{
			if(!TableParagraph()) {Close();return FALSE;}
		}
		else
		{
			if(!Paragraph()) {Close();return FALSE;}
		}
	}
	while(GetTokenAhead()!=mlstk_EndOfFile);
	GetToken();
	Close();
	return TRUE;
}
/*********************************************************************************
   Commands Proccessing
*********************************************************************************/
BOOL CMLSParser::CommandBlock()// Errors: Extra
{
	do
	{
		SkipWhiteSpaces();
		LastCommand.Empty();// Error Support
		if(GetToken()!=mlstk_Semicolon) // Not Semicolon?! Error: Extra
		{
			if(!CommandError(mpe_Extra)) return FALSE;
		}
		else
		{
			if(!Command()) return FALSE;
		}
		if(GetTokenAhead()==mlstk_NewLine) {GetToken();break;}
	}
	while(GetTokenAhead()!=mlstk_EndOfFile);
	return TRUE;
}
BOOL CMLSParser::Command()// Errors: Command
{
	char cmd[2];int n;MLSCommand Command=mlscm_Unknown;
	if(GetTokenForCommand()!=mlstk_Character)// Not a character? Report Error
		return CommandError(mpe_Command);
	cmd[0] = (char)GetTarget();
	if(cmd[0]>='a'&&cmd[0]<='z')cmd[0]-=0x20;
	if(GetTokenForCommand()!=mlstk_Character)
		return CommandError(mpe_Command);// Not a character? Report Error
	cmd[1] = (char)GetTarget();
	if(cmd[1]>='a'&&cmd[1]<='z')cmd[1]-=0x20;
	for(n=0;n<MAX_COMM;n++)
	{
		if(MLSCommandRules[n].CommandText[0]==cmd[0]&&
			MLSCommandRules[n].CommandText[1]==cmd[1])
		{Command = (MLSCommand)n;break;}
	}
	if(Command==mlscm_Unknown)// Unknown Command? Report Error
		return CommandError(mpe_Command);
	else if(cmd[1]=='J')
	{
		if(!Justify(cmd[0])) return FALSE;
	}
	else if(Command==mlscm_BF)
	{
		if(!Bold()) return FALSE;
	}
	else if(Command==mlscm_FN)
	{
		if(!Font()) return FALSE;
	}
	else if(Command==mlscm_HE)
	{
		if(!Header()) return FALSE;
	}
	else if(Command==mlscm_FT)
	{
		if(!Footer()) return FALSE;
	}
	else if(Command==mlscm_TB)
	{
		return Table();
	}
	else if(Command==mlscm_NT)
	{
		if(!Footnote()) return FALSE;
	}
	else if(Command==mlscm_PA)
	{
		if(!PageAdvance()) return FALSE;
	}
	else if(Command==mlscm_PN)
	{
		if(!PageNumber()) return FALSE;
	}
	else if(Command==mlscm_PC)
	{
		if(!PageNumberCode()) return FALSE;
	}
	else
	{
		SkipCommand();
	}
	return TRUE;
}
BOOL CMLSParser::Justify(char Just)// No Errors
{
	int x=0;
	for(;x<5;x++)
		if(Just==JustComChar[x]) break;
	if(x==4) x=-1;
	CurrentJust = CRichWriter::RichJust(x);
	return TRUE;
}
BOOL CMLSParser::Bold()// No Errors
{
	SkipWhiteSpaces();
	BOOL b=!IsEndOfCommand();
	if(b) GetToken();
	Output->SetBold(b&&GetTarget()!=0x30);
	return TRUE;
}
BOOL CMLSParser::Font()// Errors: ParaMiss
{
	int l,f;
	if((l=ParseInt())==-1)
		return CommandError(mpe_ParaMiss);
	else if(!Comma())
		return CommandError(mpe_ParaMiss);
	else if((f=ParseInt())==-1)
		return CommandError(mpe_ParaMiss);
	else if(l==2)
	{
		if(f>3) f=3;
		ArabicFont = f-1;
		if(CurrentLang==Arabic) Output->SetFont(ArabicFont);
	}
	return TRUE;
}
BOOL CMLSParser::Header()// Errors: Delemiter
{
	EmbedType OldEmbed = Embed;
	Embed = et_HeadFoot;

	Output->StartHeader();Output->StartTable();Output->StartTableDef();Output->DefCell(2867);
	Output->DefCell(2866);Output->DefCell(2867);Output->EndTableDef();SkipWhiteSpaces();

	if(GetToken()!=mlstk_Delimiter)// Error: Delemiter
		return HeaderFooterError(TRUE, OldEmbed);
	Output->StartRow();
	Output->StartCell();
	if(!Paragraph()) return FALSE;
	Output->EndCell();
	if(GetToken()!=mlstk_Delimiter)// Error: Delemiter
		return HeaderFooterError(TRUE, OldEmbed);
	Output->StartCell();
	if(!Paragraph()) return FALSE;
	Output->EndCell();
	if(GetToken()!=mlstk_Delimiter)// Error: Delemiter
		return HeaderFooterError(TRUE, OldEmbed);
	Output->StartCell();
	if(!Paragraph()) return FALSE;
	Output->EndCell();
	Output->EndRow();
	if(GetToken()!=mlstk_Delimiter)// Error: Delemiter
		return HeaderFooterError(TRUE, OldEmbed);
	Output->EndTable();
	Output->EndHeader();
	Embed = OldEmbed;
	return TRUE;
}
BOOL CMLSParser::Footer()// Errors: Delemiter
{
	EmbedType OldEmbed = Embed;
	Embed = et_HeadFoot;

	Output->StartFooter();Output->StartTable();Output->StartTableDef();Output->DefCell(2867);
	Output->DefCell(2866);Output->DefCell(2867);Output->EndTableDef();SkipWhiteSpaces();

	if(GetToken()!=mlstk_Delimiter)// Error: Delemiter
		return HeaderFooterError(FALSE, OldEmbed);
	Output->StartRow();
	Output->StartCell();
	if(!Paragraph()) return FALSE;
	Output->EndCell();
	if(GetToken()!=mlstk_Delimiter)// Error: Delemiter
		return HeaderFooterError(FALSE, OldEmbed);
	Output->StartCell();
	if(!Paragraph()) return FALSE;
	Output->EndCell();
	if(GetToken()!=mlstk_Delimiter)// Error: Delemiter
		return HeaderFooterError(FALSE, OldEmbed);
	Output->StartCell();
	if(!Paragraph()) return FALSE;
	Output->EndCell();
	Output->EndRow();
	if(GetToken()!=mlstk_Delimiter)// Error: Delemiter
		return HeaderFooterError(FALSE, OldEmbed);
	Output->EndTable();
	Output->EndFooter();
	Embed = OldEmbed;
	return TRUE;
}
BOOL CMLSParser::Table()//Errors: ParaMiss
{
	int l,r,x,ColWidth;char c;
	TotalWidth=0; ColCount=0;
	bTaleDefined = FALSE;
	// Reading Table Header
	do
	{
		SkipWhiteSpaces();
		if((l=ParseInt())==-1) return CommandError(mpe_ParaMiss);
		SkipWhiteSpaces();
		if(!Comma()) return CommandError(mpe_ParaMiss);
		SkipWhiteSpaces();
		if((r=ParseInt())==-1) return CommandError(mpe_ParaMiss);
		SkipWhiteSpaces();
		ColWidth=l-r;if(ColWidth<0) ColWidth*=-1;
		TotalWidth+=ColWidth;
		Column[ColCount].Width=ColWidth;
		if(!Comma()) return CommandError(mpe_ParaMiss);
		SkipWhiteSpaces();
		if(GetTokenForCommand()!=mlstk_Character) return CommandError(mpe_ParaMiss);
		c = GetRaw();if(c>'Z')c-=0x20;
		int n=-1;
		for(x=0;x<4;x++) if(c==JustComChar[x]) {n=x;break;}
		if(n==-1)return CommandError(mpe_Type);
		Column[ColCount].Just=CRichWriter::RichJust(n);
		SkipWhiteSpaces();
		if(!IsEndOfCommand()) if(!Comma()) return CommandError(mpe_ParaMiss);
		ColCount++;
	}
	while(!IsEndOfCommand());
	bTaleDefined = TRUE;
	return TRUE;
}
BOOL CMLSParser::Footnote()// No Errors
{
	EmbedType OldEmbed = Embed;
	Embed = et_Footnote;
	Output->AddChar('*');
	Output->StartFootnote();
	SkipWhiteSpaces();
	do
	{
		if(!Paragraph()) return FALSE;
	}
	while(GetTokenAhead()!=mlstk_Embed);
	Output->EndFootnote();
	Embed = OldEmbed;
	return TRUE;
}
BOOL CMLSParser::PageAdvance()// No Errors
{
	Output->PageAdvance();
	return TRUE;
}
BOOL CMLSParser::PageNumber()// Errors: ParaMiss
{
	int n;
	if((n=ParseInt())==-1)
		return TRUE;//CommandError(mpe_ParaMiss);
	// Commented because Anba Rafail uses ;pn without numbers ???
	Output->SetPageNumber(n);
	return TRUE;
}
BOOL CMLSParser::PageNumberCode()// Errors: ParaMiss, Expected
{
	int c;
	if((c=ParseInt())==-1)
		return CommandError(mpe_ParaMiss);
	if(c>255)
		return Error(mpe_Expected,"ASCII code");
    PageCode = char(c);
	return TRUE;
}
/*********************************************************************************
   Paragraphes/TableParagraphes
*********************************************************************************/
BOOL CMLSParser::Paragraph()//No Errors
{
	bTaleDefined = FALSE;//TODO: Debug this
	EmbedType OldEmbed = Embed;
	if(Embed==et_Block) Embed = et_Paragraph;
	if(GetTokenAhead()==mlstk_Embed&&(Embed!=et_Footnote||GetSecToken()==mlstk_Semicolon))
		if(!Embeded()) return FALSE;
	CurrentLang = GetLangAhead();
	Output->StartParagraph((CurrentLang==Arabic)?CRichWriter::Arabic:CRichWriter::English, CurrentJust);
	if(CurrentLang==Coptic) Output->SetFont(CopticFont);
	else if(CurrentLang==Arabic) Output->SetFont(ArabicFont);
	else if(CurrentLang==Greek) Output->SetFont(GreekFont);
	else Output->SetFont(EnglishFont);
	while(!IsEndOfParagraph())
	{
		if(GetTokenAhead()==mlstk_Embed)
		{
			if(!Embeded()) return FALSE;
		}
		else
		{
			if(!Character()) return FALSE;
		}
	}
	if(GetTokenAhead()==mlstk_NewLine) GetToken();
	Output->EndParagraph();
	Embed = OldEmbed;
	return TRUE;
}
BOOL CMLSParser::TableParagraph()//Errors: Expected
{
	EmbedType OldEmbed = Embed;
	Embed = et_Table;
	CRichWriter::RichJust OldJust=CurrentJust;
	Output->StartTable();
	Output->StartTableDef();
	int x=0;
	for(;x<ColCount;x++) Output->DefCell(Column[x].Width*8600/TotalWidth);
	Output->EndTableDef();
	while(GetTokenAhead()==mlstk_Delimiter)
	{
		GetToken();
		Output->StartRow();
		for(x=0;x<ColCount;x++)
		{
			CurrentJust=Column[x].Just;
			Output->StartCell();
			if(!Paragraph()) return FALSE;
			Output->EndCell();
			if(GetToken()!=mlstk_Delimiter)
				return TableParaError(TRUE, OldEmbed, OldJust);
		}
		if(GetSecToken()==mlstk_Delimiter)
		{
			if(GetToken()!=mlstk_NewLine)
				return TableParaError(FALSE, OldEmbed, OldJust);
		}
		Output->EndRow();
	}
	//bTaleDefined = FALSE; //Commented out to supply multiple tables for same difination
	Output->EndTable();
	Embed = OldEmbed;
	CurrentJust = OldJust;
	return TRUE;
}
BOOL CMLSParser::Embeded()//Errors: Expected
{
	GetToken();
	do
	{
		SkipWhiteSpaces();
		if(GetTokenAhead()==mlstk_Embed)
		{
			GetToken();
			break;
		}
		else if(GetToken()!=mlstk_Semicolon)
		{
			return Error(mpe_Expected,"Command");
		}
		if(!Command()) return FALSE;
	}
	while(GetTokenAhead()!=mlstk_EndOfFile);
	return TRUE;
}
BOOL CMLSParser::Character()//No Errors
{
	if(IsUnderLined()!=Underline)
		Output->SetUnderline(Underline=IsUnderLined());
	if(GetLangAhead()!=CurrentLang)
	{
		if(GetLangAhead()==Arabic||CurrentLang==Arabic)
			Output->SetLang((GetLangAhead()==1)?CRichWriter::Arabic:CRichWriter::English);
		CurrentLang=GetLangAhead();
		if(CurrentLang==Arabic) Output->SetFont(ArabicFont);
		else if(CurrentLang==Coptic) Output->SetFont(CopticFont);
		else if(CurrentLang==Greek) Output->SetFont(GreekFont);
		else Output->SetFont(EnglishFont);
	}
	if(GetTokenAhead()==mlstk_ZeroSpace) GetToken();
	else if(GetTokenAhead()==mlstk_HardSpace)
	{
		GetToken();
		Output->AddHardSpace();
	}
	else
	{
		GetToken();
		WCHAR chr = GetTarget();
		if(GetRaw()==PageCode&&Embed==et_HeadFoot)
			Output->AddPageNumber();
        else if(chr&0xFF00)
			Output->AddUnicodeChar(chr);
		else
			Output->AddChar((char)chr);
	}
	return TRUE;
}
BOOL CMLSParser::IsEndOfParagraph()//No Errors
{
	return GetTokenAhead()==mlstk_NewLine||
		GetTokenAhead()==mlstk_EndOfFile||
		(GetTokenAhead()==mlstk_Embed&&Embed==et_Footnote&&GetSecToken()!=mlstk_Semicolon)||
		(GetTokenAhead()==mlstk_Delimiter&&(Embed==et_HeadFoot||Embed==et_Table));
}
/*********************************************************************************
   Commands Proccessing Helpers
*********************************************************************************/
BOOL CMLSParser::IsEndOfCommand()
{
	return GetTokenAhead()==mlstk_Semicolon||
		GetTokenAhead()==mlstk_NewLine||
		GetTokenAhead()==mlstk_EndOfFile||
		(GetTokenAhead()==mlstk_Embed&&Embed!=et_Block)||
		(GetTokenAhead()==mlstk_Delimiter&&Embed==et_Table);
}
BOOL CMLSParser::IsDigit()
{
	return GetRawAhead()>=0x30&&GetRawAhead()<=0x39;
}
int CMLSParser::ParseInt()
{
	SkipWhiteSpaces();
	if(!IsDigit()) return -1;
	int x=0;
	do
	{
		GetTokenForCommand();
		x*=10;
		x+=GetRaw()-0x30;
	}
	while(IsDigit());
	return x;
}
BOOL CMLSParser::Comma()
{
	SkipWhiteSpaces();
	BOOL b = GetRawAhead()==',';
	if(b) GetTokenForCommand();
	return b;
}
void CMLSParser::SkipWhiteSpaces()
{
	while(GetRawAhead()==0x20||GetRawAhead()==0x09)
		GetToken();
}
void CMLSParser::SkipCommand()
{
	while(GetTokenAhead()!=mlstk_Semicolon&&GetTokenAhead()!=mlstk_NewLine&&
		GetTokenAhead()!=mlstk_EndOfFile&&
		(GetTokenAhead()!=mlstk_Embed||Embed==et_Block))
		
		GetTokenForCommand();
}

/*********************************************************************************
   Error Handeling
*********************************************************************************/
BOOL CMLSParser::Error(MPError nError, LPCSTR ConcMsg)
{
	BOOL bResult = FALSE;
	if(NotifyError)
	{
		bResult = NotifyError(nError,CurrentFile,GetPosition(),
			GetParaNo()+1,GetCharNo(),ErrMsg[(int)nError],ConcMsg);
	}
	return bResult;
}

BOOL CMLSParser::CommandError(MPError nError)
{
	SkipCommand();
	return Error(nError, LastCommand);
}
// For Error Support
CMLSScanner::MLSToken CMLSParser::GetTokenForCommand()
{
	CMLSScanner::MLSToken nResult = GetToken();
	LastCommand+=GetTarget();
	return nResult;
}
BOOL CMLSParser::HeaderFooterError(BOOL bHeader, EmbedType OldEmbed)
{
	SkipCommand();
	Embed = OldEmbed;
	Output->EndTable();
	(bHeader)?Output->EndHeader():Output->EndFooter();
	return Error(mpe_Expected,"Delimiter");
}
BOOL CMLSParser::TableParaError(BOOL bDelimiter, EmbedType OldEmbed, CRichWriter::RichJust OldJust)
{
	Output->EndRow();
	Output->EndTable();
	Embed = OldEmbed;
	CurrentJust = OldJust;
	return (bDelimiter)?Error(mpe_Expected,"Delimiter"):Error(mpe_Expected,"NewLine");
}
BOOL CMLSParser::DefaultHandle (MPError nError, LPCSTR Filename, ULONGLONG nPosition,
	int nParagraph, int nChar,LPCSTR Message, LPCSTR ConcMsg)
{
	if(nError==mpe_file)
	{
		AfxMessageBox("Fatal Error: Cannot open file for read.",MB_OK|MB_ICONSTOP);
		return FALSE;
	}
	//TODO: Special Code For Language Error
	CStringA ErrorTxt, tmp;
	ErrorTxt.Format("MLS Error %d at 0x%X: File \"",(int)nError,nPosition);
	tmp.Format("\", Paragraph %d, Character %d\n\n-> ",nParagraph,nChar);
	ErrorTxt+=CStringA(Filename)+tmp;
	if(nError==mpe_ParaMiss||nError==mpe_Type||nError==mpe_Command||nError==mpe_Extra)
		ErrorTxt+=CStringA(Message)+CStringA(" \"")+CStringA(ConcMsg)+CStringA("\"");
	else if(nError==mpe_Expected)
		ErrorTxt+=CStringA(ConcMsg)+CStringA(' ')+CStringA(Message);
	ErrorTxt+=CStringA("\n\nContinue Parse Process?");
	int nResult = AfxMessageBox(ErrorTxt,MB_YESNOCANCEL|MB_ICONEXCLAMATION);
	bIgnorError = (nResult==IDNO);
	return nResult==IDYES;
}